import tkinter as tk
from tkinter import filedialog, messagebox
from tkinter import ttk
import os
import time
import requests
import base64
import numpy as np
import pyaudio
import dashscope
from platformdirs import user_config_dir
import json

APP_NAME = "qwen-tts-python-gui"
CONFIG_PATH = os.path.join(user_config_dir(APP_NAME), "config.json")

VOICES = [
    ("Chelsie", "女声"),
    ("Cherry", "女声"),
    ("Ethan", "男声"),
    ("Serena", "女声"),
    ("Dylan", "北京话-男"),
    ("Jada", "吴语-女"),
    ("Sunny", "四川话-女")
]

def ensure_config():
    os.makedirs(os.path.dirname(CONFIG_PATH), exist_ok=True)
    if not os.path.exists(CONFIG_PATH):
        with open(CONFIG_PATH, 'w') as f:
            json.dump({"api_key": "", "auto_save": False, "save_folder": ""}, f)

def load_config():
    ensure_config()
    with open(CONFIG_PATH, 'r') as f:
        return json.load(f)

def save_config(config):
    with open(CONFIG_PATH, 'w') as f:
        json.dump(config, f)

class SettingsWindow(tk.Toplevel):
    def __init__(self, master, config):
        super().__init__(master)
        self.title("设置")
        self.config_data = config

        tk.Label(self, text="API Key:").grid(row=0, column=0, sticky='e')
        self.api_entry = tk.Entry(self, width=40)
        self.api_entry.insert(0, config.get("api_key", ""))
        self.api_entry.grid(row=0, column=1, padx=10, pady=5)

        self.auto_save_var = tk.BooleanVar(value=config.get("auto_save", False))
        self.auto_save_check = tk.Checkbutton(self, text="自动保存音频", variable=self.auto_save_var, command=self.toggle_folder)
        self.auto_save_check.grid(row=1, column=0, columnspan=2)

        tk.Label(self, text="保存文件夹:").grid(row=2, column=0, sticky='e')
        self.folder_entry = tk.Entry(self, width=30)
        self.folder_entry.insert(0, config.get("save_folder", ""))
        self.folder_entry.grid(row=2, column=1, sticky='w')
        tk.Button(self, text="选择...", command=self.choose_folder).grid(row=2, column=1, sticky='e')

        tk.Button(self, text="保存", command=self.save).grid(row=3, column=0, columnspan=2, pady=10)

    def choose_folder(self):
        folder = filedialog.askdirectory()
        if folder:
            self.folder_entry.delete(0, tk.END)
            self.folder_entry.insert(0, folder)

    def toggle_folder(self):
        self.folder_entry.configure(state='normal' if self.auto_save_var.get() else 'disabled')

    def save(self):
        self.config_data["api_key"] = self.api_entry.get()
        self.config_data["auto_save"] = self.auto_save_var.get()
        self.config_data["save_folder"] = self.folder_entry.get()
        save_config(self.config_data)
        self.destroy()

class App(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title("Qwen TTS GUI")
        self.geometry("500x300")
        self.config_data = load_config()

        tk.Label(self, text="请输入合成文本:").pack(pady=5)
        self.text_input = tk.Text(self, height=6, width=60)
        self.text_input.pack(pady=5)

        tk.Label(self, text="语音选择:").pack()
        self.voice_var = tk.StringVar(value=VOICES[1][0])  # 默认 Cherry
        self.voice_combo = ttk.Combobox(self, textvariable=self.voice_var, values=[f"{v[0]}（{v[1]}）" for v in VOICES], state="readonly")
        self.voice_combo.pack(pady=5)

        frame = tk.Frame(self)
        frame.pack()
        tk.Button(frame, text="播放", command=self.play_audio).pack(side="left", padx=10)
        tk.Button(frame, text="下载", command=self.download_audio).pack(side="left", padx=10)
        tk.Button(frame, text="设置", command=self.open_settings).pack(side="left", padx=10)

    def get_text(self):
        return self.text_input.get("1.0", tk.END).strip()

    def get_voice(self):
        return self.voice_var.get().split("（")[0]

    def open_settings(self):
        SettingsWindow(self, self.config_data)

    def play_audio(self):
        api_key = self.config_data.get("api_key")
        text = self.get_text()
        voice = self.get_voice()
        if not api_key or not text:
            messagebox.showerror("错误", "请填写API Key并输入文本")
            return

        try:
            p = pyaudio.PyAudio()
            stream = p.open(format=pyaudio.paInt16, channels=1, rate=24000, output=True)

            responses = dashscope.audio.qwen_tts.SpeechSynthesizer.call(
                model="qwen-tts",
                api_key=api_key,
                text=text,
                voice=voice,
                stream=True
            )
            for chunk in responses:
                audio_string = chunk["output"]["audio"]["data"]
                wav_bytes = base64.b64decode(audio_string)
                audio_np = np.frombuffer(wav_bytes, dtype=np.int16)
                stream.write(audio_np.tobytes())

            time.sleep(0.8)
            stream.stop_stream()
            stream.close()
            p.terminate()
        except Exception as e:
            messagebox.showerror("播放失败", str(e))

    def download_audio(self):
        api_key = self.config_data.get("api_key")
        text = self.get_text()
        voice = self.get_voice()
        if not api_key or not text:
            messagebox.showerror("错误", "请填写API Key并输入文本")
            return

        try:
            response = dashscope.audio.qwen_tts.SpeechSynthesizer.call(
                model="qwen-tts",
                api_key=api_key,
                text=text,
                voice=voice,
            )
            audio_url = response.output.audio["url"]

            if self.config_data.get("auto_save"):
                folder = self.config_data.get("save_folder", "")
                if not folder or not os.path.isdir(folder):
                    messagebox.showerror("保存失败", "保存路径无效")
                    return
                filename = time.strftime("%Y%m%d_%H%M%S.wav")
                save_path = os.path.join(folder, filename)
            else:
                save_path = filedialog.asksaveasfilename(defaultextension=".wav", filetypes=[("WAV文件", "*.wav")])

            if save_path:
                audio_data = requests.get(audio_url).content
                with open(save_path, 'wb') as f:
                    f.write(audio_data)
                messagebox.showinfo("成功", f"音频已保存至: {save_path}")
        except Exception as e:
            messagebox.showerror("下载失败", str(e))

if __name__ == '__main__':
    app = App()
    app.mainloop()